package com.zk.config.web.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.zookeeper.KeeperException.NoAuthException;
import org.apache.zookeeper.ZooKeeper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zk.config.api.util.ZkCom;
import com.zk.config.web.constants.Constants;
import com.zk.config.web.model.TreeData;
import com.zk.config.web.op.MyZkClient;

public class ZkCacheUtil {
	
	private static final Logger log = LoggerFactory.getLogger(ZkCacheUtil.class);
	
	public static TreeData setNodeCache(MyZkClient client, String checkPath) {
		if(client == null) return null;
		TreeData td = client.zkData.get("/");
		if(td == null) {
			td = new TreeData();
			if(client.useCache) {
				client.zkData.clear();
				client.zkData.put("/", td);
            }
			td.setText("");
			Map<String, String> state = new HashMap<>();
			state.put("opened", "true");
			td.setState(state);
			List<TreeData> ctdList = new ArrayList<>();
			td.setChildren(ctdList);
			ComUtil.getChildren(client, "/", ctdList, checkPath, client.useCache);
		}
		return td;
	}
	
	public static void createNodeSetCache(MyZkClient client, String path){
	    if(client == null || !client.useCache) return;
		int index = path.lastIndexOf("/");
		String parent = ZkCom.getZkPath(path.substring(0, index));
		TreeData td = client.zkData.get(path);
		if (td == null) return;
		td = new TreeData();
		td.setText(path.substring(index+1));
		TreeData ptd = client.zkData.get(parent);
		if(ptd == null) {
			createNodeSetCache(client, parent);
			ptd = client.zkData.get(parent);
		}
		if(ptd == null) return;
		List<TreeData> children = ptd.getChildren();
		if(children == null) {
			children = new ArrayList<>();
			ptd.setChildren(children);
		}
		if(!client.zkData.containsKey(path)) {
			client.zkData.put(path, td);
	        children.add(td);
		}
	}
	
	public static void changeNodeSetCache(MyZkClient client, String path){
	    if(client == null || !client.useCache || !client.saveNodeDataToCache) return;
	    TreeData treeData = client.zkData.get(path);
	    if(treeData != null) {
	        treeData.setData(null);
	    }
	}
	
	public static void flushChildrenSetCache(MyZkClient client, String path, boolean watch){
	    if(client == null || !client.useCache) return;
	    TreeData td = client.zkData.get(path);
	    if(td == null) return;
	    List<TreeData> ctdList = td.getChildren();
	    if(ctdList == null) return;
	    ZkCacheUtil.getChildrenSetCache(client, path, ctdList, watch);
	}
	
	public static void getChildrenSetCache(MyZkClient client, String path, List<TreeData> tdList, boolean watch){
	    if(client == null || !client.useCache) return;
	    ZooKeeper reader = client.getZooKeeper();
	    try {
	        if(reader.exists(path, watch) == null) return;
            List<String> children = reader.getChildren(path, watch);
            if (CollectionUtils.isEmpty(children)) return;
            for(String c:children) {
                String cpath = !"/".equals(path)?(ZkCom.getZkPath(path)+"/"+c):ZkCom.getZkPath(c);
                TreeData ctd = client.zkData.get(cpath);
                if(ctd == null) {
                    ctd = new TreeData();
                    ctd.setText(c);
                    List<TreeData> ctdList = new ArrayList<>();
                    ctd.setChildren(ctdList);
                    getChildrenSetCache(client, cpath, ctdList, watch);
                    if(ctdList.size() == 0) {
                        ctd.setIcon("file");
                    }
                    if(!client.zkData.containsKey(cpath)) {
                    	client.zkData.put(cpath, ctd);
                        tdList.add(ctd);
                    }
                }
            }
	    } catch (NoAuthException e) {
	        log.warn(e.getMessage());
	    } catch (Exception e) {
	        e.printStackTrace();
	        log.error(e.getMessage());
	    }
	}
	
	public static void deleteAllNodeClearCache(MyZkClient client, String path){
	    if(client == null || !client.useCache) return;
	    try {
	        if(!deleteNodeClearCache(client, path)) return;

	        Iterator<Map.Entry<String, TreeData>> it = client.zkData.entrySet().iterator();
	        while (it.hasNext()) {
	            Map.Entry<String, TreeData> entry = it.next();
	            if(ZkCom.checkPath(entry.getKey(), path)) {
	                TreeData ctd = entry.getValue();
	                it.remove();
	                if(ctd != null) {
	                    ctd.clear();
	                    ctd = null;
	                }
	            }
	        }
	    } catch (Exception e) {
	        e.printStackTrace();
	        log.error(e.getMessage());
	    }
	}

	public static boolean deleteNodeClearCache(MyZkClient client, String path){
	    boolean result = false;
	    if(client == null || !client.useCache) return result;
	    try {
	        int index = path.lastIndexOf("/");
	        String parent = ZkCom.getZkPath(path.substring(0, index));
	        String name = path.substring(index+1);
	        TreeData td = client.zkData.get(path);
	        client.zkData.remove(path);
	        TreeData ptd = client.zkData.get(parent);
	        if(td == null || ptd == null) return result;
	        List<TreeData> children = ptd.getChildren();
            if(children != null) {
                if(children.remove(td)) result = true;
                if(!result) {
                    Iterator<TreeData> it = children.iterator();
                    while(it.hasNext()) {
                        TreeData ctd = it.next();
                        if(ctd == null) continue;
                        if(name != null && name.equals(ctd.getText())) {
                            it.remove();
                            result = true;
                            ctd.clear();
                            ctd = null;
                            break;
                        }
                    }
                }
            }
	        if(result && td != null) {
	            td.clear();
	            td = null;
	        }
	    } catch (Exception e) {
	        e.printStackTrace();
	        log.error(e.getMessage());
	    }
	    return result;
	}
	
	public static void waitExistsSleep(MyZkClient client, long maxWaitTime) {
	    if(client == null || !client.useCache) return;
        long waitTime = 0;
        do {
            try {
                Thread.sleep(Constants.waitStepTime);
                waitTime += Constants.waitStepTime;
                log.debug("waitExistsSleep. waitTime=" + waitTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } while (waitTime < maxWaitTime);
	}
	
	public static void waitExistsSleep(MyZkClient client, String path) {
		if(client == null || !client.useCache) return;
        long waitTime = 0;
        do {
            try {
                Thread.sleep(Constants.waitStepTime);
                waitTime += Constants.waitStepTime;
                log.debug("waitExistsSleep. waitTime=" + waitTime + ", path=" + path);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } while (!client.zkData.containsKey(path) && waitTime < Constants.maxWaitTime);
    }
	
	public static void waitNoExistsSleep(MyZkClient client, String path) {
		if(client == null || !client.useCache) return;
        long waitTime = 0;
        do {
            try {
                Thread.sleep(Constants.waitStepTime);
                waitTime += Constants.waitStepTime;
                log.debug("waitNoExistsSleep. waitTime=" + waitTime + ", path=" + path);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } while (client.zkData.containsKey(path) && waitTime < Constants.maxWaitTime);
    }
	
	public static void waitEditSleep(MyZkClient client, String path) {
		if(client == null || !client.useCache) return;
        long waitTime = 0;
        TreeData td = client.zkData.get(path);
        while(td != null && td.getData() != null && waitTime <= Constants.maxWaitTime) {
            try {
                Thread.sleep(Constants.waitStepTime);
                waitTime += Constants.waitStepTime;
                log.debug("waitEditSleep. waitTime=" + waitTime + ", path=" + path);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
